import ADC0832                          # 导入ADC0832驱动模块
import time                             # 时间模块用于时间测量
import numpy as np                      # 数据处理库
import matplotlib.pyplot as plt         # 绘图库
from scipy.optimize import curve_fit    # 曲线拟合工具

def init():
    """硬件初始化函数，配置ADC0832模块"""
    ADC0832.setup()

def sine_func(x, A, B, C, D):
    """自定义正弦函数用于曲线拟合
    参数:
        x: 自变量（时间）
        A: 振幅
        B: 角频率（决定周期）
        C: 相位
        D: 垂直偏移量
    返回:
        正弦函数计算结果
    """
    return A * np.sin(B * x + C) + D

def loop():
    """主数据采集与处理循环"""
    n = 0                           # 数据点计数器
    y = []                          # 存储电压值的列表
    x = []                          # 存储时间戳的列表
    t_org = time.perf_counter()     # 获取高精度初始时间基准

    # 数据采集阶段：采集1000个数据点
    while n < 1000:
        digitalVal = ADC0832.getResult()        # 从ADC获取数字量（0-255）
        y.append(3.3 * float(digitalVal)/255)   # 数字量转电压值（3.3V基准电压）
        x.append(time.perf_counter() - t_org)   # 计算相对于初始时间的偏移量
        n += 1

    # 转换为numpy数组用于科学计算
    x_np = np.array(x)
    y_np = np.array(y)

    # 创建专业级图表布局
    plt.figure(figsize=(16, 8), dpi=100, facecolor='#f0f0f0')
    ax = plt.axes()
    ax.set_facecolor('#fafafa')  # 设置坐标系背景色

    # 正弦曲线拟合模块
    try:
        # 智能初始参数猜测算法
        time_span = x_np[-1] - x_np[0]  # 计算总时间跨度
        init_guess = [
            (max(y_np)-min(y_np))/2,                        # 振幅：峰峰值的一半
            2*np.pi/(time_span if time_span!=0 else 1),     # 频率：假设1个完整周期
            0,                                              # 初始相位设为0
            np.mean(y_np)                                   # 垂直偏移：取平均值
        ]
        
        # 执行最小二乘拟合（最大函数评估次数5000）
        popt, pcov = curve_fit(sine_func, x_np, y_np, p0=init_guess, maxfev=5000)
        
        # 生成高分辨率拟合曲线
        x_fit = np.linspace(x_np[0], x_np[-1], 1000)  # 生成1000个拟合点
        y_fit = sine_func(x_fit, *popt)  # 展开参数进行计算
        
        # 提取最后10个数据点对应的时间段
        last_segment_start = x_np[-10]
        mask = x_fit >= last_segment_start  # 创建布尔掩码
        
        # 绘制完整拟合曲线（橙色虚线）
        plt.plot(x_fit, y_fit, 
                color='#ff7f0e', 
                linewidth=2,
                linestyle='--',
                alpha=0.8,
                label='Fitted Sine Wave')
        
        # 突出显示最后部分的拟合效果（红色实线）
        plt.plot(x_fit[mask], y_fit[mask], 
                color='#d62728', 
                linewidth=3,
                label='Last 10 Points Fit')

    except Exception as e:
        print(f"拟合失败: {e}")
        popt = None  # 保持popt为空用于后续判断

    # 绘制原始数据曲线（蓝色半透明线）
    line = plt.plot(
        x, y,                   # X,Y数据集
        color='#2c7bb6',        # 曲线颜色（深蓝色）
        linewidth=1.2,          # 线宽
        alpha=0.6,              # 透明度（60%可见）
        label='Original Data'   # 图例标签
    )[0]
    
    # 高亮显示最后10个原始数据点（红色标记点）
    plt.scatter(
        x_np[-10:], y_np[-10:],     # 最后10个数据点
        color='#d62728',            # 点颜色（红色）
        s=60,                       # 点大小
        edgecolor='black',          # 边缘色
        zorder=3,                   # 绘制层级
        label='Last 10 Samples'     # 图例标签
    )

    # 动态坐标轴范围设置
    time_span = max(x) - min(x)       # 计算总时间跨度
    volt_span = max(y) - min(y)       # 计算电压波动范围
    # 设置坐标轴边界（X轴扩展2%，Y轴扩展10%）
    plt.xlim(min(x) - time_span*0.02, max(x) + time_span*0.02)
    plt.ylim(min(y) - volt_span*0.1, max(y) + volt_span*0.1)

    # 智能刻度格式化
    ax.xaxis.set_major_formatter(plt.FuncFormatter(lambda x, _: f'{x:.3f} s'))  # X轴格式化（保留3位小数，添加时间单位）
    ax.yaxis.set_major_formatter(plt.FuncFormatter(lambda y, _: f'{y:.2f} V'))  # Y轴格式化（保留2位小数，添加电压单位）
    # 根据时间跨度自动选择刻度密度
    plt.xticks(np.linspace(min(x), max(x), 8 if time_span>1 else 6))
    plt.yticks(np.linspace(min(y), max(y), 6))

    # 图表标注增强
    plt.xlabel('Time (seconds)', fontsize=12, fontweight='bold', color='#333333')
    plt.ylabel('Voltage (V)', fontsize=12, fontweight='bold', color='#333333')
    plt.title('High-Density Voltage Measurement with Sine Fit\nRaspberry PI ADC0832', 
             fontsize=14, pad=20, color='#2c7bb6', fontweight='bold')

    # 专业网格系统
    ax.grid(which='major', linestyle='--', linewidth=0.7, alpha=0.6)  # 主网格线
    ax.grid(which='minor', linestyle=':', linewidth=0.5, alpha=0.4)  # 次网格线
    ax.minorticks_on()  # 启用次刻度

    # 统计信息标注框
    stats_text = f'''Data Points: 1000
Max Voltage: {max(y):.2f} V
Min Voltage: {min(y):.2f} V
Time Span: {time_span:.3f} s'''  # 基础统计信息
    
    if popt is not None:
        # 添加拟合参数（保留两位小数）
        stats_text += f"\nFit Params: {popt[0]:.2f}sin({popt[1]:.2f}t+{popt[2]:.2f})+{popt[3]:.2f}"
    
    # 创建信息标注框
    ax.text(0.98, 0.15,         # 位置（坐标系相对位置，右上区域）
            stats_text,
            transform=ax.transAxes,  # 使用坐标系转换
            fontsize=10,
            verticalalignment='top',    # 顶部对齐
            horizontalalignment='right',# 右对齐
            bbox=dict(
                boxstyle='round',       # 圆角边框
                facecolor='white',      # 背景白色
                edgecolor='#cccccc',    # 边框浅灰色
                alpha=0.8))             # 80%透明度

    # 图表边框优化
    for spine in ax.spines.values():
        spine.set_color('#cccccc')  # 统一边框颜色为浅灰
        spine.set_linewidth(1)      # 边框粗细1磅
    
    # 图例配置
    plt.legend(loc='upper left',   # 位于左上角
              frameon=True,        # 显示背景
              framealpha=0.9)      # 背景90%不透明
    
    plt.tight_layout()  # 自动调整布局
    plt.show()          # 显示图表

if __name__ == '__main__':
    init()              # 初始化硬件
    loop()              # 执行主程序
    ADC0832.destroy()   # 清理GPIO资源
    print("The End")